/**
 * @file STL_YAML.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-09-10
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

#include "STL_YAML.h"
#include <string>
#include <iostream>
#include "yaml-cpp/yaml.h"

namespace STL
{
    std::string STL_YAML::getTypeName(YAML::NodeType::value type)
    {
        /*
        namespace YAML {
            struct NodeType {
                enum value { Undefined, Null, Scalar, Sequence, Map };
            };
        }
        */
        switch (type)
        {
        case YAML::NodeType::value::Null:
            return "Null";
        case YAML::NodeType::value::Scalar:
            return "Scalar";
        case YAML::NodeType::value::Sequence:
            return "Sequence";
        case YAML::NodeType::value::Map:
            return "Map";
        case YAML::NodeType::value::Undefined:
        default:
            return "Undefined";
        }
    }
    void STL_YAML::showNode(const YAML::Node &node, std::string indent /* = ""*/)
    {
        switch (node.Type())
        {
        case YAML::NodeType::value::Map:
            for (YAML::const_iterator it = node.begin(); it != node.end(); it++)
            {
                YAML::NodeType::value type = it->second.Type();
                std::cout << indent << it->first.as<std::string>();
                std::cout << "[" << STL_YAML::getTypeName(type) << "]: ";
                switch (type)
                {
                case YAML::NodeType::value::Null:
                    std::cout << "Null" << std::endl;
                    break;
                case YAML::NodeType::value::Scalar:
                    std::cout << it->second.as<std::string>() << std::endl;
                    break;
                case YAML::NodeType::value::Sequence:
                    std::cout << std::endl;
                    STL_YAML::showNode(it->second, indent + "    ");
                    break;
                case YAML::NodeType::value::Map:
                    std::cout << std::endl;
                    STL_YAML::showNode(it->second, indent + "    ");
                    break;
                case YAML::NodeType::value::Undefined:
                    std::cout << "Undefined" << std::endl;
                    break;
                default:
                    std::cout << "default TYPE:" << std::to_string(type) << std::endl;
                    break;
                }
            }
            break;
        case YAML::NodeType::value::Sequence:
            std::cout << indent << "-" << std::endl;
            for (YAML::const_iterator it = node.begin(); it != node.end(); it++)
            {
                YAML::NodeType::value type = it->Type();
                std::cout << indent << "[" << STL_YAML::getTypeName(type) << "]: ";
                switch (type)
                {
                case YAML::NodeType::value::Null:
                    std::cout << "Null" << std::endl;
                    break;
                case YAML::NodeType::value::Scalar:
                    std::cout << it->as<std::string>() << std::endl;
                    break;
                case YAML::NodeType::value::Sequence:
                    std::cout << std::endl;
                    STL_YAML::showNode(*it, indent + "    ");
                    break;
                case YAML::NodeType::value::Map:
                    std::cout << std::endl;
                    STL_YAML::showNode(*it, indent + "    ");
                    break;
                case YAML::NodeType::value::Undefined:
                    std::cout << "Undefined" << std::endl;
                    break;
                default:
                    std::cout << "default TYPE:" << std::to_string(type) << std::endl;
                    break;
                }
            }
            break;
        default:
            break;
        }
    }
    bool STL_YAML::hasSubNode(const YAML::Node &node, std::string key)
    {
        switch (node.Type())
        {
        case YAML::NodeType::value::Sequence:
            return STL_YAML::hasSubNodeOfSequence(node, std::stoul(key)); //FIXME 强制转换可能存在问题,比如""转为0,正则保证非空正整数,否则返回false
        case YAML::NodeType::value::Map:
            return STL_YAML::hasSubNodeOfMap(node, key);
        default:
            return false;
            break;
        }
    }
    bool STL_YAML::hasSubNodeOfSequence(const YAML::Node &node, size_t index)
    {
        if (!node.IsSequence())
            return false;
        return index < node.size();
    }
    bool STL_YAML::hasSubNodeOfMap(const YAML::Node &node, std::string key)
    {
        if (!node.IsMap())
            return false;
        for (YAML::const_iterator it = node.begin(); it != node.end(); it++)
        {
            std::string currentKey = it->first.as<std::string>();
            if (currentKey == key)
                return true;
        }
        return false;
    }
    bool STL_YAML::hasNodeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index)
    {
        std::string key = keys[index];
        if (index < keys.size() - 1)
        {

            if (STL_YAML::hasSubNode(node, key))
            {
                return hasNodeVectorPath(node[key], keys, index + 1);
            }
            else
            {
                return false;
            }
        }
        else
        {
            return hasSubNode(node, key);
        }
    }
    bool STL_YAML::hasNode(const YAML::Node &node, std::string path)
    {
        std::vector<std::string> keys = STL_YAML::split(path, ".");
        return hasNodeVectorPath(node, keys, 0);
    }
    YAML::NodeType::value STL_YAML::getTypeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index)
    {
        if (index < keys.size())
        {
            std::string key = keys[index];
            if (STL_YAML::hasSubNode(node, key))
            {
                return getTypeVectorPath(node[key], keys, index + 1);
            }
            else
            {
                return YAML::NodeType::value::Null;
            }
        }
        else
        {
            return node.Type();
        }
    }
    YAML::NodeType::value STL_YAML::getType(const YAML::Node &node, std::string path)
    {
        std::vector<std::string> keys = STL_YAML::split(path, ".");
        return getTypeVectorPath(node, keys, 0);
    }
    size_t STL_YAML::getSequenceSizeVectorPath(const YAML::Node &node, std::vector<std::string> &keys, size_t index)
    {
        if (index < keys.size())
        {
            std::string key = keys[index];
            if (STL_YAML::hasSubNode(node, key))
            {
                return getSequenceSizeVectorPath(node[key], keys, index + 1);
            }
            else
            {
                return 0;
            }
        }
        else
        {
            if (node.Type() == YAML::NodeType::value::Sequence)
            {
                return node.size();
            }
            else
            {
                return 0;
            }
        }
    }
    size_t STL_YAML::getSequenceSize(const YAML::Node &node, std::string path)
    {
        std::vector<std::string> keys = STL_YAML::split(path, ".");
        return getSequenceSizeVectorPath(node, keys, 0);
    }
    std::vector<std::string> STL_YAML::split(std::string str, std::string pattern)
    {
        std::string::size_type pos;
        std::vector<std::string> result;
        str += pattern;
        size_t size = str.size();

        for (int i = 0; i < (int)size; i++)
        {
            pos = str.find(pattern, i);
            if (pos < size)
            {
                std::string s = str.substr(i, pos - i);
                result.push_back(s);
                i = (int)(pos + pattern.size() - 1);
            }
        }

        return result;
    }
} // namespace STL